home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 12 / Cream of the Crop 12 (Part II) / Cream of the Crop 12 (Part II).iso / OS2 / GNUSUTIL.ZIP / src / test.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-20  |  26.3 KB  |  1,123 lines

  1. /* GNU test program (ksb and mjb) */
  2.  
  3. /* Modified to run with the GNU shell by bfox. */
  4.  
  5. /* Copyright (C) 1987-1993, 1994 Free Software Foundation, Inc.
  6.  
  7.    This file is part of GNU Bash, the Bourne Again SHell.
  8.  
  9.    Bash is free software; you can redistribute it and/or modify it under
  10.    the terms of the GNU General Public License as published by the Free
  11.    Software Foundation; either version 2, or (at your option) any later
  12.    version.
  13.  
  14.    Bash is distributed in the hope that it will be useful, but WITHOUT ANY
  15.    WARRANTY; without even the implied warranty of MERCHANTABILITY or
  16.    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  17.    for more details.
  18.  
  19.    You should have received a copy of the GNU General Public License along
  20.    with Bash; see the file COPYING.  If not, write to the Free Software
  21.    Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  22.  
  23. /* Define TEST_STANDALONE to get the /bin/test version.  Otherwise, you get 
  24.    the shell builtin version. */
  25. /* #define TEST_STANDALONE */
  26.  
  27. #include <config.h>
  28. #include <stdio.h>
  29. #include <sys/types.h>
  30.  
  31. #if !defined (TEST_STANDALONE)
  32. #  include "shell.h"
  33. #  include "posixstat.h"
  34. #  include "filecntl.h"
  35. #else /* TEST_STANDALONE */
  36. #  include "system.h"
  37. #  include "version.h"
  38. #  include "safe-stat.h"
  39. #  include "safe-lstat.h"
  40. #  include "group-member.h"
  41. #  if !defined (S_IXUGO)
  42. #    define S_IXUGO 0111
  43. #  endif /* S_IXUGO */
  44. #  if defined (_POSIX_VERSION)
  45. #    include <limits.h>
  46. #  else /* !_POSIX_VERSION */
  47. #    include <sys/param.h>
  48. #  endif /* _POSIX_VERSION */
  49. #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
  50. #define digit(c)  ((c) >= '0' && (c) <= '9')
  51. #define digit_value(c) ((c) - '0')
  52. char *program_name;
  53. #endif /* TEST_STANDALONE */
  54.  
  55. #if !defined (_POSIX_VERSION)
  56. #  include <sys/file.h>
  57. #endif /* !_POSIX_VERSION */
  58.  
  59. #include <errno.h>
  60. #if !defined (errno) && !defined(OS2)
  61. extern int errno;
  62. #endif
  63.  
  64. #if !defined (STREQ)
  65. #  define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
  66. #endif /* !STREQ */
  67.  
  68. #if !defined (member)
  69. #  define member(c, s) ((c) ? (index ((s), (c)) ? 1 : 0) : 0)
  70. #endif /* !member */
  71.  
  72. #ifndef OS2
  73. extern gid_t getgid (), getegid ();
  74. extern uid_t geteuid ();
  75. #endif
  76.  
  77. #if !defined (R_OK)
  78. #define R_OK 4
  79. #define W_OK 2
  80. #define X_OK 1
  81. #define F_OK 0
  82. #endif /* R_OK */
  83.  
  84. /* This name is used solely when printing --version information.  */
  85. #define COMMAND_NAME "test"
  86.  
  87. /* The following few defines control the truth and false output of each stage.
  88.    TRUE and FALSE are what we use to compute the final output value.
  89.    SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
  90.    TRUTH_OR is how to do logical or with TRUE and FALSE.
  91.    TRUTH_AND is how to do logical and with TRUE and FALSE..
  92.    Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
  93.     SHELL_BOOLEAN = (!value). */
  94. #define TRUE 1
  95. #define FALSE 0
  96. #define SHELL_BOOLEAN(value) (!(value))
  97. #define TRUTH_OR(a, b) ((a) | (b))
  98. #define TRUTH_AND(a, b) ((a) & (b))
  99.  
  100. #if defined (TEST_STANDALONE)
  101. #  define test_exit(val) exit (val)
  102. #else
  103.    static jmp_buf test_exit_buf;
  104.    static int test_error_return = 0;
  105. #  define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
  106. #endif /* !TEST_STANDALONE */
  107.  
  108. char *xrealloc ();
  109.  
  110. static int pos;        /* The offset of the current argument in ARGV. */
  111. static int argc;    /* The number of arguments present in ARGV. */
  112. static char **argv;    /* The argument list. */
  113.  
  114. static int unop ();
  115. static int binop ();
  116. static int unary_operator ();
  117. static int binary_operator ();
  118. static int two_arguments ();
  119. static int three_arguments ();
  120. static int posixtest ();
  121.  
  122. static int expr ();
  123. static int term ();
  124. static int and ();
  125. static int or ();
  126.  
  127. #if __GNUC__ >= 2 && defined (__GNUC_MINOR__) \
  128.     && __GNUC_MINOR__ >= 5 && !defined (__STRICT_ANSI__)
  129. #define NO_RETURN_ATTRIBUTE __attribute__ ((noreturn))
  130. #else
  131. #define NO_RETURN_ATTRIBUTE /* empty */
  132. #endif
  133.  
  134. static void test_syntax_error () NO_RETURN_ATTRIBUTE;
  135. static void beyond () NO_RETURN_ATTRIBUTE;
  136.  
  137. static void
  138. test_syntax_error (format, arg)
  139.      char *format, *arg;
  140. {
  141.   fprintf (stderr, "%s: ", argv[0]);
  142.   fprintf (stderr, format, arg);
  143.   fflush (stderr);
  144.   test_exit (SHELL_BOOLEAN (FALSE));
  145. }
  146.  
  147. /* A wrapper for stat () which disallows pathnames that are empty strings. */
  148. static int
  149. test_stat (path, finfo)
  150.      char *path;
  151.      struct stat *finfo;
  152. {
  153.   if (*path == '\0')
  154.     {
  155.       errno = ENOENT;
  156.       return (-1);
  157.     }
  158.   return (SAFE_STAT (path, finfo));
  159. }
  160.  
  161. /* Do the same thing access(2) does, but use the effective uid and gid,
  162.    and don't make the mistake of telling root that any file is
  163.    executable. */
  164. static int
  165. eaccess (path, mode)
  166.      char *path;
  167.      int mode;
  168. {
  169.   struct stat st;
  170.   static int euid = -1;
  171.  
  172.   if (test_stat (path, &st) < 0)
  173.     return (-1);
  174.  
  175.   if (euid == -1)
  176.     euid = geteuid ();
  177.  
  178. #ifndef OS2
  179.   if (euid == 0)
  180.     {
  181.       /* Root can read or write any file. */
  182.       if (mode != X_OK)
  183.     return (0);
  184.  
  185.       /* Root can execute any file that has any one of the execute
  186.      bits set. */
  187.       if (st.st_mode & S_IXUGO)
  188.     return (0);
  189.     }
  190. #endif
  191.  
  192.   if (st.st_uid == euid)        /* owner */
  193.     mode <<= 6;
  194.   else if (group_member (st.st_gid))
  195.     mode <<= 3;
  196.  
  197.   if (st.st_mode & mode)
  198.     return (0);
  199.  
  200.   return (-1);
  201. }
  202.  
  203. /* Increment our position in the argument list.  Check that we're not
  204.    past the end of the argument list.  This check is supressed if the
  205.    argument is FALSE.  Made a macro for efficiency. */
  206. #define advance(f)                            \
  207.   do                                    \
  208.     {                                    \
  209.       ++pos;                                \
  210.       if ((f) && pos >= argc)                        \
  211.     beyond ();                            \
  212.     }                                    \
  213.   while (0)
  214.  
  215. #if !defined (advance)
  216. static int
  217. advance (f)
  218.      int f;
  219. {
  220.   ++pos;
  221.  
  222.   if (f && pos >= argc)
  223.     beyond ();
  224. }
  225. #endif /* advance */
  226.  
  227. #define unary_advance()                         \
  228.   do                                    \
  229.     {                                    \
  230.       advance (1);                            \
  231.       ++pos;                                \
  232.     }                                    \
  233.   while (0)
  234.  
  235. /*
  236.  * beyond - call when we're beyond the end of the argument list (an
  237.  *    error condition)
  238.  */
  239. static void
  240. beyond ()
  241. {
  242.   test_syntax_error ("argument expected\n", (char *)NULL);
  243. }
  244.  
  245. /* Syntax error for when an integer argument was expected, but
  246.    something else was found. */
  247. static void
  248. integer_expected_error (pch)
  249.      char *pch;
  250. {
  251.   test_syntax_error ("integer expression expected %s\n", pch);
  252. }
  253.  
  254. /* Return non-zero if the characters pointed to by STRING constitute a
  255.    valid number.  Stuff the converted number into RESULT if RESULT is
  256.    a non-null pointer to a long. */
  257. static int
  258. isint (string, result)
  259.      register char *string;
  260.      long *result;
  261. {
  262.   int sign;
  263.   long value;
  264.  
  265.   sign = 1;
  266.   value = 0;
  267.  
  268.   if (result)
  269.     *result = 0;
  270.  
  271.   /* Skip leading whitespace characters. */
  272.   while (whitespace (*string))
  273.     string++;
  274.  
  275.   if (!*string)
  276.     return (0);
  277.  
  278.   /* We allow leading `-' or `+'. */
  279.   if (*string == '-' || *string == '+')
  280.     {
  281.       if (!digit (string[1]))
  282.     return (0);
  283.  
  284.       if (*string == '-')
  285.     sign = -1;
  286.  
  287.       string++;
  288.     }
  289.  
  290.   while (digit (*string))
  291.     {
  292.       if (result)
  293.     value = (value * 10) + digit_value (*string);
  294.       string++;
  295.     }
  296.  
  297.   /* Skip trailing whitespace, if any. */
  298.   while (whitespace (*string))
  299.     string++;
  300.  
  301.   /* Error if not at end of string. */
  302.   if (*string)
  303.     return (0);
  304.  
  305.   if (result)
  306.     {
  307.       value *= sign;
  308.       *result = value;
  309.     }
  310.  
  311.   return (1);
  312. }
  313.  
  314. /* Find the modification time of FILE, and stuff it into AGE, a pointer
  315.    to a long.  Return non-zero if successful, else zero. */
  316. static int
  317. age_of (filename, age)
  318.      char *filename;
  319.      long *age;
  320. {
  321.   struct stat finfo;
  322.  
  323.   if (test_stat (filename, &finfo) < 0)
  324.     return (0);
  325.  
  326.   if (age)
  327.     *age = finfo.st_mtime;
  328.  
  329.   return (1);
  330. }
  331.  
  332. /*
  333.  * term - parse a term and return 1 or 0 depending on whether the term
  334.  *    evaluates to true or false, respectively.
  335.  *
  336.  * term ::=
  337.  *    '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
  338.  *    '-'('L'|'x') filename
  339.  *     '-t' [ int ]
  340.  *    '-'('z'|'n') string
  341.  *    string
  342.  *    string ('!='|'=') string
  343.  *    <int> '-'(eq|ne|le|lt|ge|gt) <int>
  344.  *    file '-'(nt|ot|ef) file
  345.  *    '(' <expr> ')'
  346.  * int ::=
  347.  *    '-l' string
  348.  *    positive and negative integers
  349.  */
  350. static int
  351. term ()
  352. {
  353.   int value;
  354.  
  355.   if (pos >= argc)
  356.     beyond ();
  357.  
  358.   /* Deal with leading "not"'s. */
  359.   if ('!' == argv[pos][0] && '\000' == argv[pos][1])
  360.     {
  361.       value = FALSE;
  362.       while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
  363.     {
  364.       advance (1);
  365.       value ^= (TRUE);
  366.     }
  367.  
  368.       return (value ^ (term ()));
  369.     }
  370.  
  371.   /* A paren-bracketed argument. */  
  372.   if (argv[pos][0] == '(' && !argv[pos][1])
  373.     {
  374.       advance (1);
  375.       value = expr ();
  376.       if (argv[pos][0] != ')' || argv[pos][1])
  377.     test_syntax_error ("')' expected, found %s\n", argv[pos]);
  378.       advance (0);
  379.       return (TRUE == (value));
  380.     }
  381.  
  382.   /* are there enough arguments left that this could be dyadic? */
  383.   if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
  384.       ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
  385.     value = binary_operator ();
  386.  
  387.   /* Might be a switch type argument */
  388.   else if ('-' == argv[pos][0] && 0 == argv[pos][2])
  389.     {
  390.       if (unop (argv[pos][1]))
  391.     value = unary_operator ();
  392.       else
  393.     test_syntax_error ("%s: unary operator expected\n", argv[pos]);
  394.     }
  395.   else
  396.     {
  397.       value = (argv[pos][0] != '\0');
  398.       advance (0);
  399.     }
  400.  
  401.   return (value);
  402. }
  403.  
  404. static int
  405. binary_operator ()
  406. {
  407.   register int op;
  408.   struct stat stat_buf, stat_spare;
  409.   long int l, r, value;
  410.   /* Are the left and right integer expressions of the form '-l string'? */
  411.   int l_is_l, r_is_l;
  412.  
  413.   if (strcmp (argv[pos], "-l") == 0)
  414.     {
  415.       l_is_l = 1;
  416.       op = pos + 2;
  417.  
  418.       /* Make sure that OP is still a valid binary operator. */
  419.       if ((op >= argc - 1) || (binop (argv[op]) == 0))
  420.     test_syntax_error ("%s: binary operator expected\n", argv[op]);
  421.  
  422.       advance (0);
  423.     }
  424.   else
  425.     {
  426.       l_is_l = 0;
  427.       op = pos + 1;
  428.     }
  429.  
  430.   if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
  431.     {
  432.       r_is_l = 1;
  433.       advance (0);
  434.     }
  435.   else
  436.     r_is_l = 0;
  437.  
  438.   if (argv[op][0] == '-')
  439.     {
  440.       /* check for eq, nt, and stuff */
  441.       switch (argv[op][1])
  442.     {
  443.     default:
  444.       break;
  445.  
  446.     case 'l':
  447.       if (argv[op][2] == 't' && !argv[op][3])
  448.         {
  449.           /* lt */
  450.           if (l_is_l)
  451.         l = strlen (argv[op - 1]);
  452.           else
  453.         {
  454.           if (!isint (argv[op - 1], &l))
  455.             integer_expected_error ("before -lt");
  456.         }
  457.  
  458.           if (r_is_l)
  459.         r = strlen (argv[op + 2]);
  460.           else
  461.         {
  462.           if (!isint (argv[op + 1], &r))
  463.             integer_expected_error ("after -lt");
  464.         }
  465.           pos += 3;
  466.           return (TRUE == (l < r));
  467.         }
  468.  
  469.       if (argv[op][2] == 'e' && !argv[op][3])
  470.         {
  471.           /* le */
  472.           if (l_is_l)
  473.         l = strlen (argv[op - 1]);
  474.           else
  475.         {
  476.           if (!isint (argv[op - 1], &l))
  477.             integer_expected_error ("before -le");
  478.         }
  479.           if (r_is_l)
  480.         r = strlen (argv[op + 2]);
  481.           else
  482.         {
  483.           if (!isint (argv[op + 1], &r))
  484.             integer_expected_error ("after -le");
  485.         }
  486.           pos += 3;
  487.           return (TRUE == (l <= r));
  488.         }
  489.       break;
  490.  
  491.     case 'g':
  492.       if (argv[op][2] == 't' && !argv[op][3])
  493.         {
  494.           /* gt integer greater than */
  495.           if (l_is_l)
  496.         l = strlen (argv[op - 1]);
  497.           else
  498.         {
  499.           if (!isint (argv[op - 1], &l))
  500.             integer_expected_error ("before -gt");
  501.         }
  502.           if (r_is_l)
  503.         r = strlen (argv[op + 2]);
  504.           else
  505.         {
  506.           if (!isint (argv[op + 1], &r))
  507.             integer_expected_error ("after -gt");
  508.         }
  509.           pos += 3;
  510.           return (TRUE == (l > r));
  511.         }
  512.  
  513.       if (argv[op][2] == 'e' && !argv[op][3])
  514.         {
  515.           /* ge - integer greater than or equal to */
  516.           if (l_is_l)
  517.         l = strlen (argv[op - 1]);
  518.           else
  519.         {
  520.           if (!isint (argv[op - 1], &l))
  521.             integer_expected_error ("before -ge");
  522.         }
  523.           if (r_is_l)
  524.         r = strlen (argv[op + 2]);
  525.           else
  526.         {
  527.           if (!isint (argv[op + 1], &r))
  528.             integer_expected_error ("after -ge");
  529.         }
  530.           pos += 3;
  531.           return (TRUE == (l >= r));
  532.         }
  533.       break;
  534.  
  535.     case 'n':
  536.       if (argv[op][2] == 't' && !argv[op][3])
  537.         {
  538.           /* nt - newer than */
  539.           pos += 3;
  540.           if (l_is_l || r_is_l)
  541.         test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
  542.           if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
  543.         return (TRUE == (l > r));
  544.           else
  545.         return (FALSE);
  546.         }
  547.  
  548.       if (argv[op][2] == 'e' && !argv[op][3])
  549.         {
  550.           /* ne - integer not equal */
  551.           if (l_is_l)
  552.         l = strlen (argv[op - 1]);
  553.           else
  554.         {
  555.           if (!isint (argv[op - 1], &l))
  556.             integer_expected_error ("before -ne");
  557.         }
  558.           if (r_is_l)
  559.         r = strlen (argv[op + 2]);
  560.           else
  561.         {
  562.           if (!isint (argv[op + 1], &r))
  563.             integer_expected_error ("after -ne");
  564.         }
  565.           pos += 3;
  566.           return (TRUE == (l != r));
  567.         }
  568.       break;
  569.  
  570.     case 'e':
  571.       if (argv[op][2] == 'q' && !argv[op][3])
  572.         {
  573.           /* eq - integer equal */
  574.           if (l_is_l)
  575.         l = strlen (argv[op - 1]);
  576.           else
  577.         {
  578.           if (!isint (argv[op - 1], &l))
  579.             integer_expected_error ("before -eq");
  580.         }
  581.           if (r_is_l)
  582.         r = strlen (argv[op + 2]);
  583.           else
  584.         {
  585.           if (!isint (argv[op + 1], &r))
  586.             integer_expected_error ("after -eq");
  587.         }
  588.           pos += 3;
  589.           return (TRUE == (l == r));
  590.         }
  591.  
  592.       if (argv[op][2] == 'f' && !argv[op][3])
  593.         {
  594.           /* ef - hard link? */
  595.           pos += 3;
  596.           if (l_is_l || r_is_l)
  597.         test_syntax_error ("-ef does not accept -l\n", (char *)NULL);
  598.           if (SAFE_STAT (argv[op - 1], &stat_buf) < 0)
  599.         return (FALSE);
  600.           if (SAFE_STAT (argv[op + 1], &stat_spare) < 0)
  601.         return (FALSE);
  602.           return (TRUE ==
  603.               (stat_buf.st_dev == stat_spare.st_dev &&
  604.                stat_buf.st_ino == stat_spare.st_ino));
  605.         }
  606.       break;
  607.  
  608.     case 'o':
  609.       if ('t' == argv[op][2] && '\000' == argv[op][3])
  610.         {
  611.           /* ot - older than */
  612.           pos += 3;
  613.           if (l_is_l || r_is_l)
  614.         test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
  615.           if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
  616.         return (TRUE == (l < r));
  617.           return (FALSE);
  618.         }
  619.       break;
  620.     }
  621.       test_syntax_error ("unknown binary operator", argv[op]);
  622.     }
  623.  
  624.   if (argv[op][0] == '=' && !argv[op][1])
  625.     {
  626.       value = (strcmp (argv[pos], argv[pos + 2]) == 0);
  627.       pos += 3;
  628.       return (TRUE == value);
  629.     }
  630.  
  631.   if (strcmp (argv[op], "!=") == 0)
  632.     {
  633.       value = (strcmp (argv[pos], argv[pos + 2]) != 0);
  634.       pos += 3;
  635.       return (TRUE == value);
  636.     }
  637.  
  638.   /* Not reached.  */
  639.   abort ();
  640. }
  641.  
  642. static int
  643. unary_operator ()
  644. {
  645.   long r, value;
  646.   struct stat stat_buf;
  647.  
  648.   switch (argv[pos][1])
  649.     {
  650.     default:
  651.       return (FALSE);
  652.  
  653.       /* All of the following unary operators use unary_advance (), which
  654.      checks to make sure that there is an argument, and then advances
  655.      pos right past it.  This means that pos - 1 is the location of the
  656.      argument. */
  657.  
  658.     case 'a':            /* file exists in the file system? */
  659.     case 'e':
  660.       unary_advance ();
  661.       value = -1 != test_stat (argv[pos - 1], &stat_buf);
  662.       return (TRUE == value);
  663.  
  664.     case 'r':            /* file is readable? */
  665.       unary_advance ();
  666.       value = -1 != eaccess (argv[pos - 1], R_OK);
  667.       return (TRUE == value);
  668.  
  669.     case 'w':            /* File is writeable? */
  670.       unary_advance ();
  671.       value = -1 != eaccess (argv[pos - 1], W_OK);
  672.       return (TRUE == value);
  673.  
  674.     case 'x':            /* File is executable? */
  675.       unary_advance ();
  676.       value = -1 != eaccess (argv[pos - 1], X_OK);
  677.       return (TRUE == value);
  678.  
  679.     case 'O':            /* File is owned by you? */
  680.       unary_advance ();
  681.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  682.     return (FALSE);
  683.  
  684.       return (TRUE == (geteuid () == stat_buf.st_uid));
  685.  
  686.     case 'G':            /* File is owned by your group? */
  687.       unary_advance ();
  688.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  689.     return (FALSE);
  690.  
  691.       return (TRUE == (getegid () == stat_buf.st_gid));
  692.  
  693.     case 'f':            /* File is a file? */
  694.       unary_advance ();
  695.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  696.     return (FALSE);
  697.  
  698.       /* Under POSIX, -f is true if the given file exists
  699.      and is a regular file. */
  700.       return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
  701.                (0 == (stat_buf.st_mode & S_IFMT))));
  702.  
  703.     case 'd':            /* File is a directory? */
  704.       unary_advance ();
  705.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  706.     return (FALSE);
  707.  
  708.       return (TRUE == (S_ISDIR (stat_buf.st_mode)));
  709.  
  710.     case 's':            /* File has something in it? */
  711.       unary_advance ();
  712.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  713.     return (FALSE);
  714.  
  715.       return (TRUE == (stat_buf.st_size > (off_t) 0));
  716.  
  717.     case 'S':            /* File is a socket? */
  718. #if !defined (S_ISSOCK)
  719.       return (FALSE);
  720. #else
  721.       unary_advance ();
  722.  
  723.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  724.     return (FALSE);
  725.  
  726.       return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
  727. #endif                /* S_ISSOCK */
  728.  
  729.     case 'c':            /* File is character special? */
  730.       unary_advance ();
  731.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  732.     return (FALSE);
  733.  
  734.       return (TRUE == (S_ISCHR (stat_buf.st_mode)));
  735.  
  736.     case 'b':            /* File is block special? */
  737.       unary_advance ();
  738.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  739.     return (FALSE);
  740.  
  741. #ifdef OS2
  742.         return 0;
  743. #else
  744.       return (TRUE == (S_ISBLK (stat_buf.st_mode)));
  745. #endif
  746.  
  747.     case 'p':            /* File is a named pipe? */
  748.       unary_advance ();
  749. #ifndef S_ISFIFO
  750.       return (FALSE);
  751. #else
  752.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  753.     return (FALSE);
  754.       return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
  755. #endif                /* S_ISFIFO */
  756.  
  757.     case 'L':            /* Same as -h  */
  758.       /*FALLTHROUGH*/
  759.  
  760.     case 'h':            /* File is a symbolic link? */
  761.       unary_advance ();
  762. #ifndef S_ISLNK
  763.       return (FALSE);
  764. #else
  765.       /* An empty filename is not a valid pathname. */
  766.       if ((argv[pos - 1][0] == '\0') ||
  767.       (SAFE_LSTAT (argv[pos - 1], &stat_buf) < 0))
  768.     return (FALSE);
  769.  
  770.       return (TRUE == (S_ISLNK (stat_buf.st_mode)));
  771. #endif                /* S_IFLNK */
  772.  
  773.     case 'u':            /* File is setuid? */
  774.       unary_advance ();
  775. #ifndef S_ISUID
  776.       return (FALSE);
  777. #else
  778.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  779.     return (FALSE);
  780.  
  781.       return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
  782. #endif
  783.  
  784.     case 'g':            /* File is setgid? */
  785.       unary_advance ();
  786. #ifndef S_ISGID
  787.       return (FALSE);
  788. #else
  789.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  790.     return (FALSE);
  791.  
  792.       return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
  793. #endif
  794.  
  795.     case 'k':            /* File has sticky bit set? */
  796.       unary_advance ();
  797.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  798.     return (FALSE);
  799. #ifndef S_ISVTX
  800.       /* This is not Posix, and is not defined on some Posix systems. */
  801.       return (FALSE);
  802. #else
  803.       return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
  804. #endif
  805.  
  806.     case 't':            /* File (fd) is a terminal?  (fd) defaults to stdout. */
  807.       advance (0);
  808.       if (pos < argc && isint (argv[pos], &r))
  809.     {
  810.       advance (0);
  811.       return (TRUE == (isatty ((int) r)));
  812.     }
  813.       return (TRUE == (isatty (1)));
  814.  
  815.     case 'n':            /* True if arg has some length. */
  816.       unary_advance ();
  817.       return (TRUE == (argv[pos - 1][0] != 0));
  818.  
  819.     case 'z':            /* True if arg has no length. */
  820.       unary_advance ();
  821.       return (TRUE == (argv[pos - 1][0] == '\0'));
  822.     }
  823. }
  824.     
  825. /*
  826.  * and:
  827.  *    term
  828.  *    term '-a' and
  829.  */
  830. static int
  831. and ()
  832. {
  833.   int value;
  834.  
  835.   value = term ();
  836.   while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
  837.     {
  838.       advance (0);
  839.       value = TRUTH_AND (value, and ());
  840.     }
  841.   return (TRUE == value);
  842. }
  843.  
  844. /*
  845.  * or:
  846.  *    and
  847.  *    and '-o' or
  848.  */
  849. static int
  850. or ()
  851. {
  852.   int value;
  853.  
  854.   value = and ();
  855.  
  856.   while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
  857.     {
  858.       advance (0);
  859.       value = TRUTH_OR (value, or ());
  860.     }
  861.  
  862.   return (TRUE == value);
  863. }
  864.  
  865. /*
  866.  * expr:
  867.  *    or
  868.  */
  869. static int
  870. expr ()
  871. {
  872.   if (pos >= argc)
  873.     beyond ();
  874.  
  875.   return (FALSE ^ (or ()));        /* Same with this. */
  876. }
  877.  
  878. /* Return TRUE if S is one of the test command's binary operators. */
  879. static int
  880. binop (s)
  881.      char *s;
  882. {
  883.   return ((STREQ (s,   "=")) || (STREQ (s,  "!=")) || (STREQ (s, "-nt")) ||
  884.       (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
  885.       (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
  886.       (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
  887. }
  888.  
  889. /* Return non-zero if OP is one of the test command's unary operators. */
  890. static int
  891. unop (op)
  892.      int op;
  893. {
  894.   return (member (op, "abcdefgkLhprsStuwxOGnz"));
  895. }
  896.  
  897. static int
  898. two_arguments ()
  899. {
  900.   int value;
  901.  
  902.   if (STREQ (argv[pos], "!"))
  903.     value = strlen (argv[pos+1]) == 0;
  904.   else if ((argv[pos][0] == '-') && (argv[pos][2] == '\0'))
  905.     {
  906.       if (unop (argv[pos][1]))
  907.     value = unary_operator ();
  908.       else
  909.     test_syntax_error ("%s: unary operator expected\n", argv[pos]);
  910.     }
  911.   else
  912.     beyond ();
  913.   return (value);
  914. }
  915.  
  916. static int
  917. three_arguments ()
  918. {
  919.   int value;
  920.  
  921.   if (STREQ (argv[pos], "!"))
  922.     {
  923.       advance (1);
  924.       value = !two_arguments ();
  925.     }
  926.   else if (binop (argv[pos+1]))
  927.     {
  928.       value = binary_operator ();
  929.       pos = argc;
  930.     }
  931.   else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
  932.        (argv[pos][0] == '('))
  933.     value = expr ();
  934.   else
  935.     test_syntax_error ("%s: binary operator expected\n", argv[pos+1]);
  936.   return (value);
  937. }
  938.  
  939. /* This is an implementation of a Posix.2 proposal by David Korn. */
  940. static int
  941. posixtest ()
  942. {
  943.   int value;
  944.  
  945.   switch (argc - 1)    /* one extra passed in */
  946.     {
  947.       case 0:
  948.     value = FALSE;
  949.     pos = argc;
  950.     break;
  951.  
  952.       case 1:
  953.     value = strlen (argv[1]) != 0;
  954.     pos = argc;
  955.     break;
  956.  
  957.       case 2:
  958.     value = two_arguments ();
  959.     pos = argc;
  960.     break;
  961.  
  962.       case 3:
  963.     value = three_arguments ();
  964.     break;
  965.  
  966.       case 4:
  967.     if (STREQ (argv[pos], "!"))
  968.       {
  969.         advance (1);
  970.         value = !three_arguments ();
  971.         break;
  972.       }
  973.     /* FALLTHROUGH */
  974.       case 5:
  975.       default:
  976.     value = expr ();
  977.     }
  978.  
  979.   return (value);
  980. }
  981.  
  982. #if defined (TEST_STANDALONE)
  983. #include "long-options.h"
  984.  
  985. static void
  986. usage (status)
  987.      int status;
  988. {
  989.   if (status != 0)
  990.     fprintf (stderr, "Try `%s --help' for more information.\n",
  991.          program_name);
  992.   else
  993.     {
  994.       print_version("test");
  995.       printf ("\
  996. Usage: %s EXPRESSION\n\
  997.   or:  [ EXPRESSION ]\n\
  998.   or:  %s OPTION\n\
  999. ",
  1000.           program_name, program_name);
  1001.       printf ("\
  1002. \n\
  1003.   --help      display this help and exit\n\
  1004.   --version   output version information and exit\n\
  1005. \n\
  1006. EXPRESSION is true or false and sets exit status.  It is one of:\n\
  1007. ");
  1008.       printf ("\
  1009. \n\
  1010.   ( EXPRESSION )               EXPRESSION is true\n\
  1011.   ! EXPRESSION                 EXPRESSION is false\n\
  1012.   EXPRESSION1 -a EXPRESSION2   both EXPRESSION1 and EXPRESSION2 are true\n\
  1013.   EXPRESSION1 -o EXPRESSION2   either EXPRESSION1 or EXPRESSION2 is true\n\
  1014. \n\
  1015.   [-n] STRING          the length of STRING is non-zero\n\
  1016.   -z STRING            the length of STRING is zero\n\
  1017.   STRING1 = STRING2    the strings are equal\n\
  1018.   STRING1 != STRING2   the strings are not equal\n\
  1019. \n\
  1020.   INTEGER1 -eq INTEGER2   INTEGER1 is equal to INTEGER2\n\
  1021.   INTEGER1 -ge INTEGER2   INTEGER1 is greater than or equal to INTEGER2\n\
  1022.   INTEGER1 -gt INTEGER2   INTEGER1 is greater than INTEGER2\n\
  1023.   INTEGER1 -le INTEGER2   INTEGER1 is less than or equal to INTEGER2\n\
  1024.   INTEGER1 -lt INTEGER2   INTEGER1 is less than INTEGER2\n\
  1025.   INTEGER1 -ne INTEGER2   INTEGER1 is not equal to INTEGER2\n\
  1026. ");
  1027.       printf ("\
  1028. \n\
  1029.   FILE1 -ef FILE2   FILE1 and FILE2 have the same device and inode numbers\n\
  1030.   FILE1 -nt FILE2   FILE1 is newer (modification date) than FILE2\n\
  1031.   FILE1 -ot FILE2   FILE1 is older than FILE2\n\
  1032. \n\
  1033.   -G FILE     FILE exists and is owned by the effective group ID\n\
  1034.   -L FILE     FILE exists and is a symbolic link\n\
  1035.   -O FILE     FILE exists and is owned by the effective user ID\n\
  1036.   -S FILE     FILE exists and is a socket\n\
  1037.   -b FILE     FILE exists and is block special\n\
  1038.   -c FILE     FILE exists and is character special\n\
  1039.   -d FILE     FILE exists and is a directory\n\
  1040.   -e FILE     FILE exists\n\
  1041.   -f FILE     FILE exists and is a regular file\n\
  1042.   -g FILE     FILE exists and is set-group-ID\n\
  1043.   -k FILE     FILE exists and has its sticky bit set\n\
  1044.   -p FILE     FILE exists and is a named pipe\n\
  1045.   -r FILE     FILE exists and is readable\n\
  1046.   -s FILE     FILE exists and has a size greater than zero\n\
  1047.   -t          standard output is opened on a terminal\n\
  1048.   -t FD       file descriptor FD is opened on a terminal\n\
  1049.   -u FILE     FILE exists and its set-user-ID bit is set\n\
  1050.   -w FILE     FILE exists and is writable\n\
  1051.   -x FILE     FILE exists and is executable\n\
  1052. ");
  1053.       printf ("\
  1054. \n\
  1055.   --help      display this help and exit\n\
  1056.   --version   output version information and exit\n\
  1057. \n\
  1058. Beware that parentheses need to be escaped by backslashes for shells.\n\
  1059. INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
  1060. ");
  1061.     }
  1062.   exit (status);
  1063. }
  1064. #endif /* TEST_STANDALONE */
  1065.  
  1066. /*
  1067.  * [:
  1068.  *    '[' expr ']'
  1069.  * test:
  1070.  *    test expr
  1071.  */
  1072. int
  1073. #if defined (TEST_STANDALONE)
  1074. main (margc, margv)
  1075. #else
  1076. test_command (margc, margv)
  1077. #endif /* !TEST_STANDALONE */
  1078.      int margc;
  1079.      char **margv;
  1080. {
  1081.   int value;
  1082.  
  1083. #if !defined (TEST_STANDALONE)
  1084.   int code;
  1085.  
  1086.   code = setjmp (test_exit_buf);
  1087.  
  1088.   if (code)
  1089.     return (test_error_return);
  1090. #else /* TEST_STANDALONE */
  1091.   program_name = margv[0];
  1092. #endif /* TEST_STANDALONE */
  1093.  
  1094.   argv = margv;
  1095.  
  1096.   if (margv[0] && strcmp (margv[0], "[") == 0)
  1097.     {
  1098.       parse_long_options (argc, argv, COMMAND_NAME, version_string, usage);
  1099.  
  1100.       --margc;
  1101.  
  1102.       if (margc < 2)
  1103.     test_exit (SHELL_BOOLEAN (FALSE));
  1104.  
  1105.       if (margv[margc] && strcmp (margv[margc], "]") != 0)
  1106.     test_syntax_error ("missing `]'\n", (char *)NULL);
  1107.     }
  1108.  
  1109.   argc = margc;
  1110.   pos = 1;
  1111.  
  1112.   if (pos >= argc)
  1113.     usage(0);
  1114.  
  1115.   parse_long_options (argc, argv, COMMAND_NAME, version_string, usage);
  1116.   value = posixtest ();
  1117.  
  1118.   if (pos != argc)
  1119.     test_syntax_error ("too many arguments\n", (char *)NULL);
  1120.  
  1121.   test_exit (SHELL_BOOLEAN (value));
  1122. }
  1123.